package com.mz361.stdapi.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.mz361.common.utils.DateUtils;
import com.mz361.modules.system.domain.CPackage;
import com.mz361.modules.system.domain.InvoiceTitle;
import com.mz361.modules.system.domain.Invoices;
import com.mz361.modules.system.domain.PayOrder;
import com.mz361.modules.system.service.CPackageService;
import com.mz361.modules.system.service.InvoiceTitleService;
import com.mz361.modules.system.service.InvoicesService;
import com.mz361.modules.system.service.PayOrderService;
import com.mz361.stdapi.domain.PayOrderResult;
import com.mz361.stdapi.service.InvoiceMangerService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

/**
 * InvoiceMangerServiceImpl类实现了InvoiceMangerService接口，
 * 主要负责发票相关的业务逻辑处理，包括获取支付订单信息、发票信息、保存发票抬头、申请发票、取消发票等操作。
 */
@Service
@Slf4j
public class InvoiceMangerServiceImpl implements InvoiceMangerService {


    // 注入套餐服务，用于查询、操作套餐相关的业务逻辑，如根据套餐代码获取套餐信息等
    @Autowired
    CPackageService cPackageService;

    // 注入支付订单服务，用于支付订单的查询、保存、更新等操作
    @Autowired
    PayOrderService payOrderService;

    // 注入发票抬头服务，用于发票抬头的查询、保存、更新等操作
    @Autowired
    InvoiceTitleService invoiceTitleService;
    // 注入发票服务，用于发票相关的业务操作，如获取发票、保存发票等
    @Autowired
    InvoicesService invoicesService;

    /**
     * 根据用户ID获取该用户的支付订单结果列表，先通过支付订单服务获取该用户的支付订单列表，
     * 然后遍历支付订单列表，将每个支付订单信息转换为PayOrderResult对象，并设置相关属性，
     * 最后返回包含所有支付订单结果的列表。
     *
     * @param userId 用户的唯一标识，用于查询该用户对应的支付订单信息
     * @return List<PayOrderResult> 包含该用户所有支付订单结果的列表，每个PayOrderResult对象包含了订单ID、产品名称、金额、下单日期等信息
     */
    @Override
    public List<PayOrderResult> getPayOrders(String userId) {
        // 通过支付订单服务，根据用户ID获取该用户的支付订单列表
        List<PayOrder> payOrders = payOrderService.getByUserId(Long.valueOf(userId));
        // 创建一个用于存储支付订单结果的列表
        List<PayOrderResult> payOrderResults = new ArrayList<>();
        // 遍历每个支付订单，进行相关信息的转换和设置
        payOrders.forEach((k) -> {
            PayOrderResult payOrderResult = new PayOrderResult();
            // 设置支付订单结果对象的订单ID
            payOrderResult.setOrderId(k.getOrderId());
            String productName;
            if ("1".equals(k.getOrderType())) {
                productName = k.getPaidPackageTag();
            } else {
                // 通过套餐服务，根据支付订单中的套餐代码获取对应的套餐信息
                CPackage cPackage = cPackageService.getByCode(k.getPaidPackageTag());
                productName = cPackage.getName();
            }
            // 设置支付订单结果对象的产品名称，取自对应的套餐名称
            payOrderResult.setProductName(productName);
            // 设置支付订单结果对象的金额，将支付订单中的总费用转换为以元为单位（原单位可能是分，此处除以100进行转换）
            payOrderResult.setAmount(Double.valueOf(k.getTotalFee()) / 100);
            // 设置支付订单结果对象的下单日期，将支付订单中的创建日期转换为指定格式的字符串（具体格式由DateUtils.dateToStrLong方法决定）
            payOrderResult.setOrderDate(DateUtils.dateToStrLong(k.getCreateDate()));
            // 将设置好的支付订单结果对象添加到结果列表中
            payOrderResults.add(payOrderResult);
        });

        return payOrderResults;
    }

    /**
     * 根据用户ID获取该用户的所有发票信息，通过发票服务调用相应方法获取所有与该用户关联的发票列表并返回。
     *
     * @param userId 用户的唯一标识，用于查询该用户对应的发票信息
     * @return List<Invoices> 包含该用户所有发票信息的列表，Invoices对象包含了发票的各种详细信息（具体字段根据Invoices类定义）
     */
    @Override
    public List<Invoices> getInvoices(String userId) {
        // 通过发票服务，获取该用户的所有发票信息
        return invoicesService.getAllByUserId(userId);
    }

    /**
     * 保存发票抬头信息，首先设置发票抬头的会员ID以及创建日期、更新日期等信息，
     * 如果该发票抬头被设置为默认，则将该用户其他已设置为默认的发票抬头取消默认状态，
     * 最后通过发票抬头服务保存发票抬头信息并返回保存后的发票抬头对象。
     *
     * @param invoiceTitle 发票抬头对象，包含了发票抬头的各种详细信息，如抬头名称、纳税人识别号、是否默认等
     * @param userId       用户的唯一标识，用于关联发票抬头与用户，并在处理默认发票抬头时筛选该用户的其他发票抬头
     * @return InvoiceTitle 保存后的发票抬头对象，包含了最新的状态和信息（如更新后的默认状态等）
     */
    @Override
    public InvoiceTitle saveInvoiceTitle(InvoiceTitle invoiceTitle, String userId) {
        // 设置发票抬头的会员ID，将其与当前操作的用户关联起来
        invoiceTitle.setMemeberId(userId);
        // 如果发票抬头对象的ID为空，表示是新增操作，设置创建日期为当前日期
        if (null == invoiceTitle.getId()) {
            invoiceTitle.setCreateDate(new Date());
        }
        // 设置更新日期为当前日期，无论是新增还是更新操作都需要更新这个时间戳
        invoiceTitle.setUpdateDate(new Date());
        // 如果该发票抬头被设置为默认发票抬头
        if (invoiceTitle.getIsDefault()) {
            // 通过发票抬头服务获取该用户所有已设置为默认的发票抬头列表
            List<InvoiceTitle> invoiceTitles = invoiceTitleService.getAllByUserId(userId, true);
            // 遍历所有默认发票抬头列表，将它们的默认状态设置为false，即取消默认
            invoiceTitles.forEach((k) -> {
                k.setIsDefault(false);
            });
            // 批量保存更新后的发票抬头列表，将它们的默认状态在数据库中更新
            invoiceTitleService.saveAll(invoiceTitles);
        }
        // 通过发票抬头服务保存发票抬头信息，并返回保存后的发票抬头对象
        invoiceTitle = invoiceTitleService.save(invoiceTitle);
        return invoiceTitle;
    }

    /**
     * 根据传入的JSON数据和用户ID申请发票，主要流程包括：
     * 1. 解析JSON数据中的支付订单结果列表，计算发票总额，并获取要关联的支付订单列表以及设置它们的开票状态为已开票。
     * 2. 解析JSON数据获取发票抬头信息、备注信息等，创建发票对象并设置相关属性，包括发票总额、关联订单ID等。
     * 3. 保存发票信息到数据库，并批量保存更新后的支付订单信息。
     *
     * @param jsonObject 包含申请发票相关信息的JSON对象，如支付订单结果列表、发票抬头信息、备注信息等
     * @param userId     用户的唯一标识，用于关联发票与用户，并获取该用户对应的支付订单等信息
     */
    @Override
    public void applyInvoice(JSONObject jsonObject, String userId) {
        // 从JSON对象中获取支付订单结果列表
        JSONArray payOrderResults = jsonObject.getJSONArray("orders");
        // 创建一个原子引用类型的变量，用于累加计算发票总额，初始值为0.0
        AtomicReference<Double> invoiceValue = new AtomicReference<>(0.0);
        // 用于存储要关联的支付订单列表
        List<PayOrder> payOrders = new ArrayList<>();
        // 用于存储支付订单的ID列表，方便后续设置发票关联的订单信息
        List<String> orderIds = new ArrayList<>();
        // 遍历支付订单结果列表，进行相关信息处理
        payOrderResults.forEach((k) -> {
            // 将JSON对象中的每个元素转换为PayOrderResult对象
            PayOrderResult payOrderResult = JSONObject.parseObject(JSONObject.toJSONString(k), PayOrderResult.class);
            // 将支付订单结果对象的订单ID添加到订单ID列表中
            orderIds.add(payOrderResult.getOrderId());
            // 通过支付订单服务，根据订单ID获取对应的支付订单对象
            PayOrder payOrder = payOrderService.getByOrderId(payOrderResult.getOrderId());
            // 累加计算发票总额，将支付订单的总费用累加到invoiceValue中（原单位可能是分，此处未做转换，后续在设置发票金额时再做处理）
            invoiceValue.updateAndGet(v -> v + Double.valueOf(payOrder.getTotalFee()));
            // 设置支付订单的开票状态为已开票（状态值"1"表示已开票，具体状态定义需看业务逻辑中约定）
            payOrder.setInvoiceStatus("1");
            // 将处理后的支付订单对象添加到支付订单列表中
            payOrders.add(payOrder);
        });
        // 从JSON对象中获取发票抬头对象
        InvoiceTitle invoiceTitle = jsonObject.getObject("title", InvoiceTitle.class);
        // 从JSON对象中获取备注信息
        String remark = jsonObject.getString("remark");
        // 创建一个发票对象，用于保存发票相关信息
        Invoices invoices = new Invoices();
        // 设置发票的抬头名称，取自发票抬头对象的抬头名称属性
        invoices.setInvoiceTitle(invoiceTitle.getTitle());
        // 设置发票的纳税人识别号，取自发票抬头对象的纳税人识别号属性
        invoices.setCompanyNum(invoiceTitle.getTaxNumber());
        // 设置发票的类型，取自发票抬头对象的发票类型属性
        invoices.setInvoiceType(invoiceTitle.getInvoiceType());
        // 设置发票的状态为未开票（状态值"0"表示未开票，具体状态定义需看业务逻辑中约定）
        invoices.setInvoiceStatus("0");
        // 设置发票的地址，取自发票抬头对象的地址属性
        invoices.setAddress(invoiceTitle.getAddress());
        // 设置发票的金额，将计算得到的发票总额转换为以元为单位（原单位可能是分，此处除以100进行转换）
        invoices.setInvoiceValue(String.valueOf(invoiceValue.get() / 100));
        // 设置发票的银行账号，取自发票抬头对象的银行账号属性
        invoices.setBankAccount(invoiceTitle.getBankAccount());
        // 设置发票的开户银行，取自发票抬头对象的开户银行属性
        invoices.setDepositBank(invoiceTitle.getDepositBank());
        // 设置发票的邮箱，取自发票抬头对象的邮箱属性
        invoices.setEmail(invoiceTitle.getEmail());
        // 设置发票的联系电话，取自发票抬头对象的联系电话属性
        invoices.setPhone(invoiceTitle.getPhone());
        // 设置发票的申请日期为当前日期
        invoices.setApplyDate(DateUtils.getCurrDate());
        // 设置发票的创建日期为当前日期
        invoices.setCreateDate(new Date());
        // 设置发票的会员ID，将其与当前操作的用户关联起来
        invoices.setMemeberId(userId);
        // 设置发票的备注信息
        invoices.setRemark(remark);
        // 将关联的支付订单ID列表转换为JSON字符串后设置到发票对象中，方便后续查询发票关联的订单信息
        invoices.setOrderIds(JSONObject.toJSONString(orderIds));
        // 通过发票服务保存发票信息到数据库
        invoicesService.save(invoices);
        // 通过支付订单服务批量保存更新后的支付订单信息到数据库
        payOrderService.saveAll(payOrders);
    }

    /**
     * 根据发票ID和用户ID取消发票，主要操作包括：
     * 1. 根据发票ID获取发票信息，解析出关联的支付订单ID列表。
     * 2. 遍历支付订单ID列表，修改每个关联支付订单的开票状态为未开票。
     * 3. 更新发票的状态为取消状态（此处状态值设为3，具体状态含义需看业务逻辑中约定），并保存发票信息到数据库。
     *
     * @param invoiceId 要取消的发票的唯一标识，用于获取发票详细信息以及关联的支付订单信息
     * @param userId    用户的唯一标识，用于验证操作权限等（此处未体现具体验证逻辑，可根据业务需求补充）
     */
    @Override
    public void cancelInvoice(Long invoiceId, String userId) {
        // 通过发票服务，根据发票ID获取发票详细信息
        Invoices invoices = invoicesService.getById(invoiceId);
        // 解析发票对象中的关联支付订单ID列表
        List<String> orderIds = JSONObject.parseObject(invoices.getOrderIds(), List.class);
        // 遍历每个关联支付订单的ID，修改支付订单的开票状态为未开票
        for (String orderid : orderIds) {
            PayOrder payOrder = payOrderService.getByOrderId(orderid);
            payOrder.setInvoiceStatus("0");
            payOrderService.save(payOrder);
        }
        // 设置发票的状态为取消状态（此处状态值设为3，具体状态含义需看业务逻辑中约定）
        Byte status = 3;
        invoices.setStatus(status);
        // 通过发票服务保存更新后的发票信息到数据库
        invoicesService.save(invoices);
    }

    /**
     * 根据用户ID获取该用户的所有发票抬头信息，通过发票抬头服务调用相应方法获取所有与该用户关联的发票抬头列表并返回。
     *
     * @param userId 用户的唯一标识，用于查询该用户对应的发票抬头信息
     * @return List<InvoiceTitle> 包含该用户所有发票抬头信息的列表，每个InvoiceTitle对象包含了发票抬头的各种详细信息，如抬头名称、纳税人识别号、是否默认等
     */
    @Override
    public List<InvoiceTitle> getInvoiceTitles(String userId) {
        // 通过发票抬头服务，获取该用户的所有发票抬头信息
        return invoiceTitleService.getAllByUserId(userId);
    }

    /**
     * 根据发票抬头ID和用户ID删除发票抬头信息，通过发票抬头服务调用相应方法根据ID和用户ID删除对应的发票抬头信息。
     *
     * @param id     要删除的发票抬头的唯一标识
     * @param userId 用户的唯一标识，用于验证操作权限以及确定要删除的是该用户的发票抬头信息
     */
    @Override
    public void delInvoiceTitle(String id, String userId) {
        invoiceTitleService.delByIdAndUserId(Long.valueOf(id), userId);
    }
}